package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.gulimall.common.to.OrderTo;
import com.atguigu.gulimall.common.to.mq.StockDetailTo;
import com.atguigu.gulimall.common.exception.NoStockException;
import com.atguigu.gulimall.common.utils.R;
import com.atguigu.gulimall.ware.config.RabbitConfig;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.enums.LockStatusEnum;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.common.to.SkuHashStockVo;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.common.to.mq.StockLockedTo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.WareSkuLockVo;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private OrderFeignService orderFeignService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private WareOrderTaskDetailService orderTaskDetailService;

    @Autowired
    private ProductFeignService productFeignService;


    public void handleStockLOckRelease(StockLockedTo lockedTo) throws IOException {
        System.out.println("接收到解锁库存的消息");
        StockDetailTo detailTo = lockedTo.getDetailTo();
        /**
         * 解锁：
         *  1. 查询数据库关于这个订单的锁定库存信息
         *  有：证明库存锁定成功
         *  无：
         *   1. 没有这个订单，表示订单系统回滚，必须解锁
         *   2. 有这个订单，
         *     2.1 订单状态为已取消，解锁库存
         *     2.2 没取消，不解锁
         *
         *  // 我们必须启动手动ack，否则不管库存系统是否回滚成功，消息都会被删除
         */
        // 远程查询订单系统
        Long detailId = detailTo.getId();
        WareOrderTaskDetailEntity byId = orderTaskDetailService.getById(detailId);
        if (byId != null) { // 判断订单详情是否存在
            Long taskId = lockedTo.getId();
            WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(taskId);
            R r = orderFeignService.hasOrderByOrderSn(taskEntity.getOrderSn());
            Integer code = (Integer) r.get("code");
            if (code == 0) {
                OrderVo orderVo = (OrderVo) r.getData("orderEntity", new TypeReference<OrderVo>() {
                });
                if (orderVo == null || orderVo.getStatus() == 4) {
                    // 表示order为null或者订单状态为已关闭，订单系统回滚了，我们需要回滚
                    if (byId.getLockStatus() == LockStatusEnum.LOCK.getCode()) {
                        rollbackWareLock(detailTo.getSkuId(), detailTo.getWareId(), detailTo.getSkuNum(), detailTo.getId());
                    }
                } else {
                    throw new RuntimeException("远程调用失败");
                }
            }
        } else {
            // 表示库存系统自己回滚了，我们不需要操作

        }
    }

    /**
     * 避免订单发送给mq时卡顿，导致库存系统发送给mq比订单系统快
     * 就会导致库存系统不会解锁订单（判断为4（已关闭才回滚库存））
     *
     * @param orderTo
     */
    @Override
    public void OrderCloseNoQuick(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        WareOrderTaskEntity taskEntity = wareOrderTaskService.getTaskBySn(orderSn);
        Long id = taskEntity.getId();
        List<WareOrderTaskDetailEntity> detailEntities = orderTaskDetailService.listByTaskIdAndLockStatus(id, LockStatusEnum.LOCK.getCode());
        for (WareOrderTaskDetailEntity detailEntity : detailEntities) {
            if (detailEntity.getLockStatus() == LockStatusEnum.LOCK.getCode()) {
                rollbackWareLock(detailEntity.getSkuId(), detailEntity.getWareId(), detailEntity.getSkuNum(), detailEntity.getId());
            }
        }
    }

    // 库存回滚方法
    @Transactional
    public void rollbackWareLock(Long skuId, Long wareId, Integer skuNum, Long detailId) {
        // 库存解锁
        baseMapper.rollbackWareLock(skuId, wareId, skuNum, detailId);
        // 更新库存工作单的状态
        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
        detailEntity.setLockStatus(LockStatusEnum.UNLOCK.getCode());
        detailEntity.setId(detailId);
        orderTaskDetailService.updateById(detailEntity);

    }


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /**
         *  skuId
         *  wareId
         */
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<WareSkuEntity>();
        String skuId = (String) params.get("skuId");
        if (StringUtils.isNotEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }

        String wareId = (String) params.get("wareId");
        if (StringUtils.isNotEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        // 判断是否有这个库存记录
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
            WareSkuEntity entity = new WareSkuEntity();
            entity.setSkuId(skuId);
            entity.setWareId(wareId);
            entity.setStock(skuNum);
            entity.setStockLocked(0);
            //TODO 远程调用查询sku名称
            try {
                // 当只有 skuName 远程调用查询出错时，我们不让此方法回滚（或者使用 sentinel）
                R info = productFeignService.info(skuId);
                // 得到 R 中的 data
                Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                if ((Integer) info.get("code") == 0) {
                    entity.setSkuName((String) skuInfo.get("skuName"));
                }
                info.get("skuInfo");

            } catch (Exception e) {

            }
            entity.setSkuName("");
            this.save(entity);
        } else {
            wareSkuDao.addStock(skuId, wareId, skuNum);
        }
    }

    @Override
    public WareSkuEntity getBySkuId(Long skuId) {
        WareSkuEntity wareSkuEntity = this.getOne(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId));
        return wareSkuEntity;
    }

    @Override
    public List<SkuHashStockVo> getSkusHasStock(List<Long> skuIds) {
        List<SkuHashStockVo> collect = skuIds.stream().map(skuId -> {
            SkuHashStockVo skuHashStockVo = new SkuHashStockVo();
            // 查询当前sku的总库存量
//            SELECT SUM(stock - stock_locked) FROM `wms_ware_sku` WHERE sku_id = 1;
            Long count = baseMapper.getSkuStock(skuId);
            skuHashStockVo.setSkuId(skuId);
            skuHashStockVo.setHasStock(count == null ? false : count > 0);
            return skuHashStockVo;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 锁库存
     *
     * @param vo
     * @return
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {

        /**
         * 保存库存工作单的详情
         * 用于分布式事务
         */
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskService.save(taskEntity);


        // 找到每个商品在哪个仓库都有库存
        List<OrderItemVo> locks = vo.getLocks();

        List<SkuWareHasStock> collect = locks.stream().map(item -> {
            SkuWareHasStock wareHasStock = new SkuWareHasStock();
            wareHasStock.setSkuId(item.getSkuId());
            wareHasStock.setNum(item.getCount());
            List<Long> wareIds = wareSkuDao.selectWareIdIdHasStock(item.getSkuId());
            wareHasStock.setWareId(wareIds);
            return wareHasStock;
        }).collect(Collectors.toList());

        // 锁定库存
        for (SkuWareHasStock hs : collect) {
            boolean flag = false; //标志位
            Long skuId = hs.getSkuId();
            List<Long> wareId = hs.getWareId();
            if (wareId == null || wareId.size() == 0) {
                throw new NoStockException(skuId);
            }
            for (Long wd : wareId) {
                // 成功返回1
                Long line = wareSkuDao.lockSkuStock(skuId, wd, hs.getNum());
                if (line == 1) {
                    flag = true;
                    // TODO：库存锁定成功，告诉MQ
                    WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity(null, skuId, "", hs.getNum(), taskEntity.getId(), wd, LockStatusEnum.LOCK.getCode());
                    orderTaskDetailService.save(detailEntity);
                    StockLockedTo lockedTo = new StockLockedTo();
                    lockedTo.setId(taskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo();
                    BeanUtils.copyProperties(detailEntity, stockDetailTo);
                    lockedTo.setDetailTo(stockDetailTo);
                    // 发送信息
                    rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE, RabbitConfig.NORMAL_ROUTINGKEY, lockedTo);
                    break;
                }
            }
            // 表示所有仓库都没锁住
            if (!flag) {
                throw new NoStockException(skuId);
            }
        }

        return true;
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private Integer num;
        private List<Long> wareId;
    }

}